package cn.mjnxin.iot.gate;

import java.io.File;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

import cn.mjnxin.iot.base.cache.ProtocalStrategyCache;
import cn.mjnxin.iot.base.domain.NodeConfig;
import cn.mjnxin.iot.base.domain.ProtocolConfig;
import cn.mjnxin.iot.base.log.LogWorker;
import cn.mjnxin.iot.base.util.ConfigTool;
import cn.mjnxin.iot.gate.cache.GateRuntimeCache;
import cn.mjnxin.iot.gate.cache.GateConfigCache;
import cn.mjnxin.iot.gate.connector.GateAsClient2Host;
import cn.mjnxin.iot.gate.connector.GateAsClient2Terminal;
import cn.mjnxin.iot.gate.connector.GateAsServer4Host;
import cn.mjnxin.iot.gate.connector.GateAsServer4Terminal;
import cn.mjnxin.iot.gate.sender.GateDataSender;
import cn.mjnxin.iot.gate.sender.HexMessageSender;
import cn.mjnxin.iot.gate.util.GateUtil;
import cn.mjnxin.iot.gate.worker.Gate2HostWorker;
import cn.mjnxin.iot.gate.worker.Gate2TerminalWorker;
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timeout;
import io.netty.util.TimerTask;

/**
 * 网关主类入口
 * @author CLG 2023年10月10日
 * @version V1.0.0
 */
public class Gate {
	private static boolean isCluster   = false;
	private static boolean devMode     = false;

	public static void start(String[] args) {
		parseConfig(args.length > 0 ? args[0] : null);
		GateUtil.bannel();
		// 初始化缓存
		GateRuntimeCache.init(ProtocalStrategyCache.protocalStrategyCache.keySet().iterator());
		System.setProperty("org.jboss.netty.epollBugWorkaround", "true");
		initEnvriment();
		if (isCluster) {
//			try {
//				locks.await();
//			} catch (InterruptedException e1) {
//				e1.printStackTrace();
//			} catch (Exception e) {
//				e.printStackTrace();
//			}
		}

		start4Host();
		
		//跟前置连接后等待一下再接受terminal接入
		try {
			Thread.sleep(2000L);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		start4Terminal(GateRuntimeCache.gateNo);
		startMonitor();
		if (devMode) new Thread(new HexMessageSender(new GateDataSender()), "Gate Hex Data Sender").start();

		/**
		 * kill pid时 该方法会自动执行
		 */
		addShutdownHook();
	}

	private static void startMonitor() {
		HashedWheelTimer wheelTimer = new HashedWheelTimer();
		wheelTimer.newTimeout(new TimerTask() {
			public void run(Timeout timeout) throws Exception {
				GateRuntimeCache.monitorCache();
			}
		}, 60, TimeUnit.SECONDS);
	}

	/**
	 * 环境初始化
	 */
	private static void initEnvriment() {
		// 初始化数据中转线程
		try {
			Gate2HostWorker.init(GateRuntimeCache.hostThreads);
			Gate2TerminalWorker.init(GateRuntimeCache.terminalThreads);
		} catch (Exception e) {
			System.err.println("数据中转线程启动失败");
			e.printStackTrace();
			System.exit(-1);
		}
	}

	/**
	 * JVM的关闭钩子--JVM正常关闭才会执行
	 */
	private static void addShutdownHook() {
		Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
			public void run() {
				shutdown();
			}
		}));
	}

	private static void parseConfig(String baseDir) {
        final String workDir;
        final String CONFIG_FILE_NAME = "iot-gate.conf";
		if (baseDir == null) {
        	String currentDir = System.getProperty("user.dir");
        	if (!new File(currentDir).isDirectory()) {
        		throw new RuntimeException(currentDir + " 当前目录没有读权限");
        	}
            if (new File(currentDir + "\\src\\main\\resources").isDirectory()){
            	workDir = currentDir  + File.separatorChar + "src" +  File.separatorChar + "main" + File.separatorChar + "resources" + File.separatorChar;
            }
            else if (new File(currentDir + "\\config").isDirectory()){
            	workDir = currentDir + File.separatorChar + "config" + File.separatorChar;
            }
            else {
            	//默认取工作目录
            	workDir = currentDir;
            }
        }
        else {
        	if (baseDir.endsWith(CONFIG_FILE_NAME)) {
        		baseDir.substring(0, baseDir.length() - CONFIG_FILE_NAME.length());
        	}
        	if (!new File(baseDir).isDirectory()) {
        		throw new RuntimeException(baseDir + " 指定目录没有读权限");
        	}
        	workDir = baseDir;
        }
        
    	if (!new File(workDir).isDirectory()) {
    		throw new RuntimeException(workDir + " 目录没有读权限");
    	}
    	
		try {
			Map<String, Object> config = ConfigTool.readConfigParams(workDir + CONFIG_FILE_NAME);
			@SuppressWarnings("unchecked")
			Map<String, String> common = (Map<String, String>) config.remove("common");
			parseCommonConfig(common);
			parseProtocol(config);
		} catch (Exception e) {
			e.printStackTrace();
			System.err.println("配置文件加载失败");
			System.exit(-1);
		}
	}

	public static void shutdown() {
		// 清空缓存信息
		LogWorker.logForce("网关正常关闭前执行  停止Worker...............................");
		for(Iterator<Entry<Integer, GateAsServer4Terminal>> itr = GateConfigCache.gateAsServer4TerminalMap.entrySet().iterator(); itr.hasNext(); ) {
			LogWorker.logForce("GateAsServer4Terminal close()");
			itr.next().getValue().close();
		}
		for(Iterator<Entry<String, GateAsClient2Terminal>> itr = GateConfigCache.gateAsClient2TerminalMap.entrySet().iterator(); itr.hasNext(); ) {
			LogWorker.logForce("GateAsClient4Terminal close()");
			itr.next().getValue().close();
		}
		LogWorker.logForce("Gate2ClientWorker.getInstance().stopWorker()");
		Gate2TerminalWorker.getInstance().stopWorker();

		for(Iterator<Entry<Integer, GateAsServer4Host>> itr = GateConfigCache.gateAsServer4HostMap.entrySet().iterator(); itr.hasNext(); ) {
			LogWorker.logForce("GateAsServer4Host close()");
			itr.next().getValue().close();
		}
		for(Iterator<Entry<String, GateAsClient2Host>> itr = GateConfigCache.gateAsClient2HostMap.entrySet().iterator(); itr.hasNext(); ) {
			LogWorker.logForce("GateAsClient2Host close()");
			itr.next().getValue().close();
		}
//		if (-1 != GateChannelCache.gatePort) {
//			LogWorker.logForce("gateKernel4Host.close()");
//			gateAsServer4Host.close();
//		}
		LogWorker.logForce("Gate2HostWorker.getInstance().stopWorker()");
		Gate2HostWorker.getInstance().stopWorker();

		int times = 10;
		while (!Gate2HostWorker.getInstance().isShutdown() || !Gate2TerminalWorker.getInstance().isShutdown()) {
			try {
				Thread.sleep(1000L);
			} catch (InterruptedException e) {
			}
			times--;
			if (times < 0) {
				break;
			}
		}

		LogWorker.logForce(String.format("网关正常关闭前执行  清空所有缓存信息 times%s...............................", times));
		GateRuntimeCache.clearAll();
		GateRuntimeCache.clearAll();
	}
	
	private static void parseCommonConfig(Map<String, String> common) {
		GateRuntimeCache.gateNo = Integer.parseInt(common.get("gateNo"));
		LogWorker.logForce(String.format("网关编号为：%s", GateRuntimeCache.gateNo));
		GateRuntimeCache.terminalThreads = Integer.parseInt(common.get("terminalThreads"));
		GateRuntimeCache.hostThreads = Integer.parseInt(common.get("hostThreads"));
		LogWorker.logable = !ProtocalStrategyCache.parseBoolean(common.get("nolog"));
		
		isCluster = ProtocalStrategyCache.parseBoolean(common.get("cluster"));
		devMode = ProtocalStrategyCache.parseBoolean(common.get("devMode"));
	}

	public static void parseProtocol(Map<String, Object> config) {
		try {
			System.out.println("配置项个数：" + config.size());
			for (String key : config.keySet()) {
				if (key.startsWith("terminal")) {
					@SuppressWarnings("unchecked")
					Map<String, String> paramMap = (Map<String, String>) config.get(key);
					if (paramMap.containsKey("enabled") && !ProtocalStrategyCache.parseBoolean(paramMap.get("enabled"))) {
						continue;
					}

					ProtocolConfig protocolConfig = ProtocalStrategyCache.parseProtocolConfig(paramMap);
					ProtocalStrategyCache.protocalStrategyCache.put(protocolConfig.getPid(), protocolConfig);
				}
				else if (key.startsWith("host")) {
					if (isCluster) {
					}
					@SuppressWarnings("unchecked")
					Map<String, String> paramMap = (Map<String, String>) config.get(key);
					if (paramMap.containsKey("enabled") && !ProtocalStrategyCache.parseBoolean(paramMap.get("enabled"))) {
						continue;
					}
					boolean workAsClient = Boolean.parseBoolean(paramMap.get("workAsClient"));
					String hostAddress = paramMap.get("address");
					Integer pid = null;
					if(workAsClient) {
						Integer.parseInt(paramMap.get("pid"));
					}
					int port = Integer.parseInt(paramMap.get("port"));
					int heartbeat = paramMap.containsKey("heartbeat") ? 2 : Integer.parseInt(paramMap.get("heartbeat"));
					NodeConfig nodeConfig = new NodeConfig(workAsClient, GateRuntimeCache.gateNo, pid, hostAddress, port);
					nodeConfig.setHeartbeat(heartbeat);
					nodeConfig.setLogLevel(paramMap.containsKey("logLevel") ? Integer.parseInt(paramMap.get("logLevel")) : 0);
					GateConfigCache.nodeConfigMap.put(hostAddress, nodeConfig);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			System.err.println("配置文件加载失败");
			System.exit(-1);
		}
	}

	private static void start4Host() {
		for (Iterator<Entry<String, NodeConfig>> itr = GateConfigCache.nodeConfigMap.entrySet().iterator(); itr.hasNext();) {
			NodeConfig nodeConfig = itr.next().getValue();
			if (nodeConfig.isWorkAsClient()) {
				startAsClient2Host(nodeConfig);
			}
			else {
				startAsServer4Host(nodeConfig);
			}
		}
	}
	
	private static void startAsClient2Host(NodeConfig nodeConfig) {
		// 启动与前置对接的客户端 因为是阻塞运行 需要开线程启动
		final Integer gateNo = nodeConfig.getKeyNo();
		final Integer pid = nodeConfig.getPid();
		final String hostAddresses = nodeConfig.getAddress();
		final Integer heartbeat = nodeConfig.getHeartbeat();
		String[] array = hostAddresses.split(",", -1);
		for(String address : array) {
			String[] addressPort = address.split(":", -1);
			final String ip = addressPort[0];
			final int port;
			if (addressPort.length > 1) {
				port = Integer.parseInt(addressPort[1]);
			}
			else {
				port = nodeConfig.getPort();
			}
			new Thread(new Runnable() {
				public void run() {
					GateAsClient2Host gateAsClient2Host = new GateAsClient2Host(gateNo, pid, ip, port, heartbeat, nodeConfig.getLogLevel(), true);
					GateConfigCache.gateAsClient2HostMap.put(ip + ":" + port, gateAsClient2Host);
					gateAsClient2Host.open();
				}
			}, "gateAsClient2HostThread_ip_" + ip + ":" + port).start();
		}
	}

	private static void startAsServer4Host(NodeConfig nodeConfig) {
		// 启动与前置对接的客户端 因为是阻塞运行 需要开线程启动
		final Integer port = nodeConfig.getPort();
		final Integer heartbeat = nodeConfig.getHeartbeat();
		new Thread(new Runnable() {
			public void run() {
				LogWorker.logForce(String.format("网关Server模式，开始提供前置连接服务，端口：%d，心跳周期：%d M", port, heartbeat));
				GateAsServer4Host gateAsServer4Host = new GateAsServer4Host(port, heartbeat, nodeConfig.getLogLevel());
				GateConfigCache.gateAsServer4HostMap.put(port, gateAsServer4Host);
				gateAsServer4Host.bindAddress(gateAsServer4Host.config());
			}
		}, "gateAsServer4HostThread_kernel_0_" + port).start();
	}

	private static void start4Terminal(Integer gateNo) {
		for (Iterator<Entry<Integer, ProtocolConfig>> itr = ProtocalStrategyCache.protocalStrategyCache.entrySet().iterator(); itr.hasNext();) {
			final ProtocolConfig protocolConfig = itr.next().getValue();
			if (protocolConfig.isWorkAsClient()) {
				startAsClient2Terminal(gateNo, protocolConfig);
			}
			else {
				startAsServer4Terminal(protocolConfig);
			}
		}
	}
	
	private static void startAsClient2Terminal(Integer gateNo, ProtocolConfig protocolConfig) {
		// 启动与前置对接的客户端 因为是阻塞运行 需要开线程启动
		final String terminalAddresses = protocolConfig.getAddress();
		String[] array = terminalAddresses.split(",", -1);
		for(String address : array) {
			String[] addressPort = address.split(":", -1);
			final String ip = addressPort[0];
			final int port;
			if (addressPort.length > 1) {
				port = Integer.parseInt(addressPort[1]);
			}
			else {
				port = protocolConfig.getProtocolPort();
			}
			new Thread(new Runnable() {
				public void run() {
					GateAsClient2Terminal gateAsClient2Terminal = new GateAsClient2Terminal(gateNo, ip, port, protocolConfig, true);
					GateConfigCache.gateAsClient2TerminalMap.put(ip + ":" + port, gateAsClient2Terminal);
					gateAsClient2Terminal.open();
				}
			}, "gateAsClient2TerminalThread_ip_" + ip + ":" + port).start();
		}
	}

	private static void startAsServer4Terminal(ProtocolConfig protocolType) {
		final int pid = protocolType.getPid();
		final int protocolPort = protocolType.getProtocolPort();
		// 启动与硬件终端对接的服务端 因为是阻塞运行 需要开线程启动---后续版本中会变动
		new Thread(new Runnable() {
			public void run() {
				LogWorker.logForce(String.format("网关开始提供协议类型为%s的终端连接服务，端口：%s，心跳周期：%sS", pid, protocolPort, protocolType.getHeartbeat()));
				GateAsServer4Terminal gateAsServer4Terminal = new GateAsServer4Terminal(pid, protocolPort, protocolType.getHeartbeat());
				GateConfigCache.gateAsServer4TerminalMap.put(pid, gateAsServer4Terminal);
				gateAsServer4Terminal.bindAddress(gateAsServer4Terminal.config(protocolType));
			}
		}, "gateAsServer4TerminalThread_pid_" + protocolType.getPid()).start();
	}
}
